<?php

namespace app\service;

use app\exception\ErrnoPlus;
use Exception;
use share\exception\ApiException;
use share\exception\Errno;
use share\service\AmqpService;
use share\service\cache\CommonCacheKeyService;
use share\service\IdMakerService;
use share\service\MQService;
use share\service\RedisLockService;
use share\service\UserBaseService;
use share\utils\Helper;
use think\Log;

class OpenAppOrderService{

    public static function getOrderList($openAppId,$filter,$page,$size){
        if(isset($filter['user_id'])){
            $filter['user_id'] = $filter['user_id'];
            unset($filter['user_id']);
        }
        if(isset($filter['sn'])){
            $filter['sn'] = $filter['sn'];
            unset($filter['sn']);
        }
        $filter['open_app_id'] = $openAppId;

        $list = OssOrderService::getNativeAll($filter,[],'created DESC',$size,($page -1 ) * $size);
        $count = OssOrderService::count($filter);
        return [
            'list' => $list,
            'total' => $count,
        ];
    }

    public static function getOrderDetail($openAppId,$filter){
        if(isset($filter['order_no'])){
            $filter['order_no'] = $filter['order_no'];
            unset($filter['order_no']);
        }
        if(isset($filter['out_trade_no'])){
            $filter['out_trade_no'] = $filter['out_trade_no'];
            unset($filter['out_trade_no']);
        }
        $filter['open_app_id'] = $openAppId;
        $order = OssOrderService::getByFilter($filter);
        if($order){
            $orderDetail = OssOrderDetailService::getByFilter(['oss_order_id' => $order['id']]);
            $payed = OssAliyunPayedProductService::getByFilter(['order_id' => $order['id']]);
            return [
                'order' => $order,
                'detail' => $orderDetail,
                'payed' => $payed,
            ];
        }
        return null;
    }

    public static function getOrderByOutTradeNo($openAppId,$outTradeNo){
        $filter = [
            'open_app_id' => $openAppId,
            'out_trade_no' => $outTradeNo,
        ];
        return OssOrderService::getByFilter($filter);
    }

    /**
     * 下单
     */
    public static function create($openAppId,$orderInfo){   
        $sn = $orderInfo['sn'];
        $sku = $orderInfo['sku'];
        $userId = $orderInfo['user_id'];
        $extend = isset($orderInfo['extend']) ? $orderInfo['extend'] : [];

        $outTradeNo = $orderInfo['out_trade_no'];
        $originOrder = self::getOrderByOutTradeNo($openAppId,$outTradeNo);
        if(!empty($originOrder)){
            throw new ApiException("已存在相同的订单号.请勿重复下单",ErrnoPlus::OSS_ORDER_ALREADY_EXIST);
        }
        $userInfo = UserBaseService::getByUserId($userId);
        if(empty($userInfo)){
            throw new ApiException("下单用户不存在 {$userId}",Errno::COMMON_INVALID_INPUT);
        }
        $uid = $userInfo['id'];
        $product = OssProductService::getBySku($openAppId,$sku);
        if(empty($product)){
            throw new ApiException("该套餐不存在或者您无权访问.",ErrnoPlus::OSS_PRODUCT_NOT_FOUND);
        }
        if($product['state'] != OssProductService::STATE_OF_ON_SHELF){
            throw new ApiException("该套餐已下架.不能继续购买",ErrnoPlus::OSS_PRODUCT_STATE_INVALID);
        }
        $num = intval($product['num']);
        if($num < 0 || $num > 100){
            throw new ApiException("不允许的购买数量",ErrnoPlus::COMMON_INVALID_INPUT);
        }

        $cloudStorage = AliyunCloudStorageService::getBase($product['aliyun_cloud_storage_id']);
        if(empty($cloudStorage)){
            throw new ApiException("该套餐未找到",Errno::COMMON_INVALID_INPUT);
        }
        $deviceBase = DeviceBaseService::getUsefulOssSn($sn);
        if(empty($deviceBase) || empty($deviceBase['iot_id']) || empty($deviceBase['server_site'])){
            throw new ApiException("设备不存在或者参数已丢失",Errno::COMMON_INVALID_INPUT);
        }
        $iotId = $deviceBase['iot_id'];
        // 使用设备站点 而不是云存套餐站点
        $site = $deviceBase['server_site'];
        $cloudInfo = [
            'iotId' => $iotId,
            'commodityCode' => $cloudStorage['commodity_code'],
            'specification' => $cloudStorage['specification'],
            'site'  => $site,
        ];
        $userBind = UserDeviceBindingService::getBindingInfoByIotId($iotId);
        if($userBind == null){
            throw new ApiException("未找到 iotId:{$iotId} 相关的信息.",ErrnoPlus::INVALID_PARAM);
        }
        if($userBind['uid'] != $uid){
            throw new ApiException("用户{$uid}不能为设备 iotId:{$iotId} 购买套餐.",ErrnoPlus::INVALID_PARAM);
        }
        $check = self::check($openAppId,$cloudInfo);
        if(!$check){
            throw new ApiException("该设备不能购买指定的套餐.",ErrnoPlus::OSS_ORDER_CREATE_NOT_ALLOW);
        }
        
        $orderId = IdMakerService::make(IdMakerService::NAME_OF_OSS_ORDER);
        //1位 阿里云站点 3位套餐id 10位日期时间 5位随机数 1位 预留位
        $orderNo = sprintf("%d%s%s%d%d",AliyunCloudStorageService::getSiteNum(
            $site),
            str_pad($cloudStorage['id'],3,'0',STR_PAD_LEFT), 
            date("YmdHis"),
            mt_rand(10000,99999),
            1
        );
        $order = [
            'id'=> $orderId,
            'order_no' => $orderNo,
            'open_app_id' => $openAppId,
            'out_trade_no' => $outTradeNo,
            'product_id' => $product['id'],
            'aliyun_cloud_storage_id' => $cloudStorage['id'],
            'state' => OssOrderService::STATE_OF_NOT_PAY,
            'money' =>intval($product['price']),
            'uid' => $uid,
            'sn' => $sn,   
            'iot_id' => $iotId,            
        ];
        $orderDetail  = OssOrderDetailService::getDetailInfo($extend);
        $orderDetail['oss_order_id'] = $orderId;
        $orderDetail['oss_product_id'] = $product['id'];
        $orderDetail['oss_product_sku'] =  $product['sku'];
        $orderDetail['oss_product_name'] = $product['name'];
        $orderDetail['price'] = $product['price'];
        $orderDetail['num'] = $num;
        $orderDetail['immediate_use'] = 0; // 不立即使用  往后顺延 
        $orderDetail['lifecycle'] = $cloudStorage['lifecycle'];
        $orderDetail['months'] = $cloudStorage['months'];
        $orderDetail['record_type'] = $cloudStorage['record_type'];
        $orderDetail['commodity_code'] = $cloudStorage['commodity_code'];
        $orderDetail['specification'] = $cloudStorage['specification'];
        $orderDetail['site'] = $site;
        OssOrderService::add($order);
        OssOrderDetailService::add($orderDetail);
        return [
            'order' => $order,
            'detail' => $orderDetail,
        ];
    }

    /**
     * 检查是否可以购买(下单)
     */
    public static function check($openAppId,$params){
        $projectId = AliyunService::getDefaultProjectId();
        $aliyunHandle = AliyunService::instance($projectId,$params['site']);
        $res = $aliyunHandle->isCanBuyCloudStorage($params['iotId'],$params['commodityCode'],$params['specification']);
    
        if($res && isset($res['available']) && $res['available']){
            return true;
        }
        else{
            return false;
        }
    }

    /**
     * 支付/购买
     */
    public static function buy($openAppId,$outTradeNo){
        $order = OssOrderService::getBase([
            'out_trade_no' => $outTradeNo
        ]);
        if(empty($order)){
            throw new ApiException("订单不存在.",ErrnoPlus::OSS_ORDER_NOT_EXIST);
        }
        $orderId = $order['id'];
        $orderDetail = OssOrderDetailService::getByFilter(['oss_order_id' => $orderId]);
        if(empty($orderDetail)){
            throw new ApiException("订单信息缺失.",ErrnoPlus::OSS_ORDER_NOT_EXIST);
        }
        if($order['open_app_id'] != $openAppId){
            throw new ApiException("您购买订单不存在.",ErrnoPlus::OSS_ORDER_NOT_EXIST);
        }
        if($order['state'] != OssOrderService::STATE_OF_NOT_PAY){
            AmqpService::simplePush([
                'service' => 'app\\service\\OpenAppOrderService@orderAsync',
                'params' => [
                     $order['iot_id']
                ],
            ]);
      
            throw new ApiException("订单支付状态不正确.请稍后重试",ErrnoPlus::OSS_ORDER_STATE_INVALID);
        }
        
        $lockName = CommonCacheKeyService::getCacheKey(CommonCacheKeyService::CACHE_KEY_LOCK_OF_OSS_ORDER_PAY, ['id' => $orderId]);
        if (!RedisLockService::getLock($lockName,10)){
            throw new ApiException("指定的订单正在支付中.请勿重新购买.",ErrnoPlus::OSS_ORDER_STATE_INVALID);
        }

        OssOrderService::update($orderId,['state' => OssOrderService::STATE_OF_PART_PAY]);
        $tn = time();
        $payNo = '';
        try{
           $payNo = self::doBuy($order['iot_id'],$order['uid']. '000000',$orderDetail);
           if($payNo){
                OssOrderService::update($orderId,[
                    'state' => OssOrderService::STATE_OF_PAYED,
                    'pay_order_no' => $payNo,
                    'pay_time' => $tn,
                ]);
           }
           else{
                OssOrderService::update($orderId,[
                    'state' => OssOrderService::STATE_OF_NOT_PAY,
                ]);
            }
        }catch(Exception $e){
            trace("BuyOssOrderError ".$e->getMessage()."\n".json_encode(debug_backtrace()),Log::ERROR);
            OssOrderService::update($orderId,[
                'state' => OssOrderService::STATE_OF_NOT_PAY,
            ]);
            //OssOrderService::update($orderId,['state' => OssOrderService::STATE_OF_NOT_PAY]);
            RedisLockService::releaseLock($lockName);
            throw new ApiException($e->getMessage(),ErrnoPlus::OSS_ORDER_PAY_ERROR);
        }
        RedisLockService::releaseLock($lockName);
        AmqpService::simplePush([
            'service' => 'app\\service\\OpenAppOrderService@orderAsync',
            'params' => [
                 $order['iot_id']
            ],
        ]);
        return [
            'payOrderNo' => $payNo,
            'orderId' => $orderId,
        ];
    }

    /**
     * 下单后立即购买
     */
    public static function createAndBuy($openAppId,$orderInfo){
        $outTradeNo = $orderInfo['out_trade_no'];
        $originOrder = self::getOrderByOutTradeNo($openAppId,$outTradeNo);
        if(empty($originOrder)){ // 订单不存在
            $orderInfo = self::create($openAppId,$orderInfo);
        }
        else{
            $detail = OssOrderDetailService::getBase($originOrder['id']);
            $orderInfo = [
                'order' => $originOrder,
                'detail' => $detail,
            ];
        }
        
        $order = $orderInfo['order'];
        $orderDetail = $orderInfo['detail'];
        if($order['open_app_id'] != $openAppId){
            throw new ApiException("您购买订单不存在.",ErrnoPlus::OSS_ORDER_NOT_EXIST);
        }
        if($order['state'] != OssOrderService::STATE_OF_NOT_PAY){
            AmqpService::simplePush([
                'service' => 'app\\service\\OpenAppOrderService@orderAsync',
                'params' => [
                     $order['iot_id']
                ],
            ]);
            throw new ApiException("订单支付状态不正确.请稍后重试",ErrnoPlus::OSS_ORDER_STATE_INVALID);
        }
        $orderId = $order['id'];
        $lockName = CommonCacheKeyService::getCacheKey(CommonCacheKeyService::CACHE_KEY_LOCK_OF_OSS_ORDER_PAY, ['id' => $orderId]);
        if (!RedisLockService::getLock($lockName,10)){
            throw new ApiException("指定的订单正在支付中.请勿重新购买.",ErrnoPlus::OSS_ORDER_STATE_INVALID);
        }
        $orderId = $order['id'];

        OssOrderService::update($orderId,['state' => OssOrderService::STATE_OF_PART_PAY]);
        $tn = time();
        $payNo = '';
        try{
           $payNo = self::doBuy($order['iot_id'],$order['uid'] . '000000',$orderDetail);
           if($payNo){
                OssOrderService::update($orderId,[
                    'state' => OssOrderService::STATE_OF_PAYED,
                    'pay_order_no' => $payNo,
                    'pay_time' => $tn,
                ]);
           }
           else{
                OssOrderService::update($orderId,[
                    'state' => OssOrderService::STATE_OF_NOT_PAY,
                ]);
           }
        }catch(Exception $e){
            trace("createAndBuyOssOrderError ".$e->getMessage()."\n".json_encode(debug_backtrace()),Log::ERROR);
            //OssOrderService::update($orderId,['state' => OssOrderService::STATE_OF_NOT_PAY]);
            OssOrderService::update($orderId,[
                'state' => OssOrderService::STATE_OF_NOT_PAY,
            ]);
            RedisLockService::releaseLock($lockName);
            throw new ApiException($e->getMessage(),ErrnoPlus::OSS_ORDER_PAY_ERROR);
        }
        RedisLockService::releaseLock($lockName);
        AmqpService::simplePush([
            'service' => 'app\\service\\OpenAppOrderService@orderAsync',
            'params' => [
                 $order['iot_id']
            ],
        ]);
        // MQService::push([
        //     'service' => 'app\\service\\OpenAppOrderService@orderAsync',
        //    'params' => [
        //         $order['iot_id']
        //    ],
        // ], MQService::CHANNEL_OF_ACCOUNT);
        return [
            'order' => $order,
            'orderDetail' => $orderDetail,
            'payOrderNo' => $payNo,
        ];
    }

    /**
     * 去阿里云购买
     */
    public static function doBuy($iotId,$userId,$orderDetail){
        $projectId = AliyunService::getDefaultProjectId();
        $aliyunHandle = AliyunService::instance($projectId,$orderDetail['site']);
        $extend = [
            "enableDefaultPlan" => $orderDetail["enable_default_plan"] == 1 ? true : false,
            "immediateUse" => $orderDetail["immediate_use"] == 1 ? true : false,
            "preRecordDuration" => $orderDetail["pre_record_duration"],
            "recordDuration" => $orderDetail["record_duration"],
            "eventRecordProLong" => $orderDetail["event_record_pro_long"] == 1 ? true : false,
            "maxRecordFileDuration" => $orderDetail["max_record_file_duration"], 
        ];
        $res = $aliyunHandle->buyCloudStorage($iotId,$userId,$orderDetail['commodity_code'],$orderDetail['specification'],$orderDetail['num'],$extend);
        if($res && isset($res['orderId'])){
            return $res['orderId'];
        }
        else{
            return null;
        }
    }

    /**
     * 立即启用某个订单/套餐
     */
    public static function immediateUse($iotId,$orderId){

    }

    /**
     * 同步设备的套餐使用情况
     */
    public static function orderAsync($iotId,$checkBind = false){
       
        $deviceBase = DeviceBaseService::getByFilter(['iot_id' => $iotId]);
        if(empty($deviceBase)){
             //TODO  for debug
             $projectId = AliyunService::getDefaultProjectId();
             $aliyunHandle = AliyunService::instance($projectId,'CN');

            //  TODO for work
            //  trace("no device info for iotId {$iotId} where async oss order");
            //  throw new ApiException("no device info for iotId {$iotId} where async oss order",Errno::COMMON_SYSTEM_ERROR);
        }
        else{

            $projectId = $deviceBase['aliyun_project_id'];
            $serverSite = $deviceBase['server_site'];
            $project = AliyunProjectService::fastGetById($projectId);
            if(empty($project) || empty($project['project_key']) || empty($serverSite)){
                trace("device info of iotId {$iotId} aliyun_project_id or server_site is empty where async oss order");
                throw new ApiException("device info of iotId {$iotId} aliyun_project_id or server_site is empty where async oss order",Errno::COMMON_SYSTEM_ERROR);
            }
            $aliyunHandle = AliyunService::instance($project['project_key'],$serverSite);
        }
        $waitBuyOrders = OssOrderService::getAllByFilter([
            'iot_id' => $iotId,
            'state' => OssOrderService::STATE_OF_PART_PAY,
        ]);

        if(count($waitBuyOrders) > 0) {
            try{
                foreach($waitBuyOrders as $order){
                    self::buy($order['open_app_id'],$order['out_trade_no']);
                }
            }catch(Exception $e){
                trace("RechargeBuyOssOrderError ".$e->getMessage()."\n".json_encode(debug_backtrace()),Log::ERROR);
            }
        }
        $datas = [];
        $page = 0;
        $size = 50;
        do{
            $res = $aliyunHandle->cloudStorageOrderQuery($iotId,$page,$size,$checkBind);
            if($res && $res['orderList']) {
                foreach($res['orderList'] as $order){
                    if($order['paymentStatus'] == 1){ // 只要已支付的
                        $datas[] = $order;
                    }
                }
                if($page + 1 < $res['pageCount']){
                    $page++;
                }
                else{
                    break;
                }
            }
            else{
                break;
            }
        }while(true);

        $tn = time();
        if(count($datas) > 0){
            $payOrderNos = array_column($datas,'orderId');
            $originOrders = OssOrderService::getAllByFilter([
                'pay_order_no' => ['in',$payOrderNos],
            ]);
            $payedInfos = OssAliyunPayedProductService::getAllByFilter([
                'order_id' => ['in',array_column($originOrders,'id')],
            ]);
            $addPayedInfos = [];
            $updatePayedInfos = [];
            foreach($datas as $item){
                $order = Helper::filterOne($originOrders,['pay_order_no' => $item['orderId']]);             
                if(empty($order)){
                    trace("ossOrder get by aliyun but not exists in oss_detail model".json_encode($item),Log::ERROR);
                }
                else{
                    $payedInfo = Helper::filterOne($payedInfos,['order_id' => $order['id']]);        
                    $money = intval(bcmul($item['price'] , 100));
                    $status = $item['status'] == 0 ? 1 : 2;
                    $startTime = strtotime($item['startTime']);
                    $endTime = strtotime($item['endTime']);
                    if(empty($payedInfo)){
                        $info = [
                            'open_app_id' => $order['open_app_id'],
                            'iot_id' => $item['iotId'],
                            'order_id' => $order['id'],
                            'order_no' => $item['outOrderNo'],
                            'pay_order_no' => $item['orderId'],
                            'money' => $money ,
                            'uid' => $order['uid'],
                            'status' => $status,
                            'start_time' => $startTime,
                            'end_time' => $endTime,
                            'consume_time' => $item['preConsume'],
                            'record_type' => $item['recordType'],
                            'created' => $tn,
                            'updated' => $tn,
                        ];
                        $addPayedInfos[] = $info;
                    }
                    else{
                        $update = [];
                        if($payedInfo['iot_id'] != $item['iotId']){
                            $update['iot_id'] = $item['iotId'];
                        }
                        if($payedInfo['order_no'] != $item['outOrderNo']){
                            $update['order_no'] = $item['outOrderNo'];
                        }
                        if($payedInfo['pay_order_no'] != $item['orderId']){
                            $update['pay_order_no'] = $item['orderId'];
                        }
                        
                        if($payedInfo['money'] !=  $money ){
                            $update['money'] =  $money;
                        }
                        if($payedInfo['status'] !=  $status ){
                            $update['status'] =  $status;
                        }
                        if($payedInfo['start_time'] !=  $startTime ){
                            $update['start_time'] =  $startTime;
                        }
                        if($payedInfo['end_time'] !=  $endTime ){
                            $update['end_time'] =  $endTime;
                        }


                        // if($payedInfo['consume_time'] !=  $item['preConsume'] ){
                        //     $update['consume_time'] =  $item['preConsume'];
                        // }
                        if($payedInfo['record_type'] !=  $item['recordType'] ){
                            $update['record_type'] =  $item['recordType'];
                        }
                        if(count($update) > 0){
                            if($payedInfo['consume_time'] !=  $item['preConsume'] ){
                                $update['consume_time'] =  $item['preConsume'];
                            }
                            $updatePayedInfos[$payedInfo['id']] = $update;
                        }
                    }
                }
            }
            if(count($addPayedInfos) > 0){
                OssAliyunPayedProductService::addBulk($addPayedInfos);
            }
            if(count($updatePayedInfos) > 0 ){
                foreach($updatePayedInfos as $id => $item){
                    OssAliyunPayedProductService::update($id,$item);
                }
            }
        }                
        return $datas;
    }

    /**
     * 已购套餐列表
     */
    public static function payedInfo($openAppId,$sn){
        $device = DeviceBaseService::getUsefulOssSn($sn);
        if(empty($device)){
            return null;
        }

        $iotId = $device['iot_id'];
        AmqpService::simplePush([
            'service' => 'app\\service\\OpenAppOrderService@orderAsync',
            'params' => [
                $iotId
            ],
        ]);

        // MQService::push([
        //     'service' => 'app\\service\\OpenAppOrderService@orderAsync',
        //    'params' => [
        //         $iotId
        //    ],
        // ], MQService::CHANNEL_OF_ACCOUNT);

        $payedInfos = OssAliyunPayedProductService::getAllByFilter([
            'iot_id' => $iotId,
        ]);
        if(count($payedInfos) < 1){
            return null;
        }
        $ossOrders = OssOrderService::getAll(array_column($payedInfos,'order_id'));
        $res = [];
        foreach($payedInfos as $payed){
            $order = Helper::filterOne($ossOrders,['id' => $payed['order_id']]);
            if(empty($order)){ // TODO 错误数据 可能不是在当前环境购买的
                continue;
            }
            if($order['open_app_id'] !== $openAppId){// 无权查看
                continue;
            }
            $item = [
                'sn' => $sn,
                'orderNo' => $order['order_no'],
                'outTradeNo' => $order['out_trade_no'],
                //'aliyun_cloud_storage_id' => $order['aliyun_cloud_storage_id'],
                'money' => $order['money'],
                'uid' => $order['uid'],
                'iotId' => $order['iot_id'],
                'status' => $payed['status'],
                'startTime' => $payed['start_time'],
                'endTime' => $payed['end_time'],
                'consumeTime' => $payed['consume_time'],
                'recordType' => $payed['record_type'],                
            ];
            $res[] = $item;
        }  

        return $res;
    }
    
}